package com.atom.test.opengl.utils;

import android.opengl.Matrix;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

public class MatrixState {
    private static float[] mProjMatrix = new float[16];
    private static float[] mVMatrix = new float[16];
    private static float[] mMVPMatrix;
    private static float[][] mStack = new float[10][16];
    private static int stackTop = -1;
    private static float[] currMatrix;

    public static float[] setInitStack() {
        if (currMatrix == null) {
            currMatrix = new float[16];
            Matrix.setIdentityM(currMatrix , 0);
        }
        return currMatrix;
    }

    public static float[] pushMatrix() {
        stackTop++;
        for (int i = 0; i < 16; i++) {
            mStack[stackTop][i] = currMatrix[i];
        }
        return currMatrix;
    }

    public static float[] popMatrix() {
        for (int i = 0; i < 16; i++) {
            currMatrix[i] = mStack[stackTop][i];
        }
        stackTop--;
        return currMatrix;
    }

    private static float[] mVMatrixForSpecFrame = new float[16];

    public static void copyMVMatrix() {
        for (int i = 0; i < 16; i++) {
            mVMatrixForSpecFrame[i] = mVMatrix[i];
        }
    }

    public static void matrix(float[] self) {
        float[] result = new float[16];
        Matrix.multiplyMM(result, 0, currMatrix, 0, self, 0);
        currMatrix = result;
    }

    // 矩阵平移
    public static float[] translate = new float[3];

    public static void translate(float x, float y, float z) {
        translate(currMatrix, x, y, z);
    }

    public static void translate(float[] matrix, float x, float y, float z) {
        Matrix.translateM(matrix, 0, x, y, z);
    }

    // 矩阵旋转
    public static float[] rotate = new float[3];

    public static void rotate(float angle, float x, float y, float z) {
        rotate(currMatrix, angle, x, y, z);
    }

    public static void rotate(float[] matrix, float angle, float x, float y, float z) {
        Matrix.rotateM(matrix, 0, angle, x, y, z);
    }

    // 矩阵缩放
    public static float[] scale = new float[3];

    public static void scale(float x, float y, float z) {
        scale(currMatrix, x, y, z);
    }

    public static void scale(float[] matrix, float x, float y, float z) {
        Matrix.scaleM(matrix, 0, x, y, z);
    }

    public static float[] getMMatrix() {
        return currMatrix;
    }


    // 投影矩阵
    public static float[] projectLocation = new float[6];

    public static void setProjectOrtho(
            float left, float right,
            float bottom, float top,
            float near, float far
    ) {
        setProjectOrtho(mProjMatrix, 0, left, right, bottom, top, near, far);
    }

    public static void setProjectOrtho(//正交投影
            float[] matrix, int offset,
            float left, float right,
            float bottom, float top,
            float near, float far
    ) {
        Matrix.orthoM(matrix, offset, left, right, bottom, top, near, far);
    }


    public static void setProjectFrustum(
            float left, float right,
            float bottom, float top,
            float near, float far
    ) {
        setProjectFrustum(mProjMatrix, 0, left, right, bottom, top, near, far);
    }

    public static void setProjectFrustum(//透视投影
            float[] matrix, int offset,
            float left, float right,
            float bottom, float top,
            float near, float far
    ) {
        Matrix.frustumM(matrix, offset, left, right, bottom, top, near, far);
    }


    //摄像机位置
    public static float[] cameraLocation = new float[]{0, 0, 0};
    public static FloatBuffer cameraLocationFB;

    public static void setCamera(
            float cx, float cy, float cz,
            float tx, float ty, float tz,
            float upx, float upy, float upz
    ) {
        llbbL.clear();
        setCamera(mVMatrix, 0, cx, cy, cz, tx, ty, tz, upx, upy, upz);
        cameraLocation[0] = cx;
        cameraLocation[1] = cy;
        cameraLocation[2] = cz;
        cameraLocationFB = llbbL.order(ByteOrder.nativeOrder()).asFloatBuffer();
        cameraLocationFB.put(cameraLocation).position(0);
    }

    public static void setCamera(
            float[] floats, int offset,
            float cx, float cy, float cz,
            float tx, float ty, float tz,
            float upx, float upy, float upz
    ) {
        Matrix.setLookAtM(floats, offset, cx, cy, cz, tx, ty, tz, upx, upy, upz);
    }

    public static float[] getInvertMvMatrix() {
        float[] invM = new float[16];
        Matrix.invertM(invM, 0, mVMatrix, 0);
        return invM;
    }

    public static float[] fromPtoPreP(float[] p) {
        float[] inverM = getInvertMvMatrix();
        float[] preP = new float[4];
        Matrix.multiplyMV(preP, 0, inverM, 0, new float[]{p[0], p[1], p[2], 1}, 0);
        return new float[]{preP[0], preP[1], preP[2]};
    }

    //光源位置 平行光
    static ByteBuffer llbbL = ByteBuffer.allocateDirect(3 * 4);
    public static float[] lightLocation = new float[]{0, 0, 0};
    public static FloatBuffer lightLocationFB;

    public static void setLightLocation(float x, float y, float z) {
        llbbL.clear();
        lightLocation[0] = x;
        lightLocation[1] = y;
        lightLocation[2] = z;
        lightLocationFB = llbbL.order(ByteOrder.nativeOrder()).asFloatBuffer();
        lightLocationFB.put(lightLocation).position(0);
    }

    //光源位置 点光
    public static float[] lightDirection = new float[]{0, 0, 1};
    public static FloatBuffer lightDirectionFB;

    public static void setLightDirection(float x, float y, float z) {
        llbbL.clear();
        lightDirection[0] = x;
        lightDirection[1] = y;
        lightDirection[2] = z;
        llbbL.order(ByteOrder.nativeOrder());
        lightDirectionFB = llbbL.asFloatBuffer();
        lightDirectionFB.put(lightDirection);
        lightDirectionFB.position(0);
    }


    //最终矩阵
    public static float[] getFinalMatrix() {
        return getFinalMatrix(currMatrix);
    }

    public static float[] getFinalMatrix(float[] spec) {
        mMVPMatrix = getFinalMatrix(spec, mVMatrix, mProjMatrix);
        return mMVPMatrix;
    }

    public static float[] getFinalMatrix(float[] spec, float[] VMatrix) {
        float[] mMVPMatrix = new float[16];
        Matrix.multiplyMM(mMVPMatrix, 0, VMatrix, 0, spec, 0);
        return mMVPMatrix;
    }

    public static float[] getFinalMatrix(float[] spec, float[] VMatrix, float[] project) {
        float[] mMVPMatrix = new float[16];
        Matrix.multiplyMM(mMVPMatrix, 0, VMatrix, 0, spec, 0);
        Matrix.multiplyMM(mMVPMatrix, 0, project, 0, mMVPMatrix, 0);
        return mMVPMatrix;
    }


    public static String printMatrix(float[] matrix) {
        StringBuilder builder = new StringBuilder();
        builder.append("| ");
        builder.append(matrix[0]).append(" ,");
        builder.append(matrix[1]).append(" ,");
        builder.append(matrix[2]).append(" ,");
        builder.append(matrix[3]);
        builder.append(" |\n");


        builder.append("| ");
        builder.append(matrix[0 + 4]).append(" ,");
        builder.append(matrix[1 + 4]).append(" ,");
        builder.append(matrix[2 + 4]).append(" ,");
        builder.append(matrix[3 + 4]);
        builder.append(" |\n");


        builder.append("| ");
        builder.append(matrix[0 + 8]).append(" ,");
        builder.append(matrix[1 + 8]).append(" ,");
        builder.append(matrix[2 + 8]).append(" ,");
        builder.append(matrix[3 + 8]);
        builder.append(" |\n");


        builder.append("| ");
        builder.append(matrix[0 + 12]).append(" ,");
        builder.append(matrix[1 + 12]).append(" ,");
        builder.append(matrix[2 + 12]).append(" ,");
        builder.append(matrix[3 + 12]);
        builder.append(" |\n");
        return builder.toString();
    }

}
